*&---------------------------------------------------------------------*
*&  Include           ZXMLLIB
*&---------------------------------------------------------------------*
*CLASS cl_ixml DEFINITION LOAD.

CLASS lcl_reading_list DEFINITION CREATE PRIVATE.
   PUBLIC SECTION.
      CLASS-METHODS:
         class_constructor,
         create_new_list  IMPORTING im_topic TYPE string
                          RETURNING VALUE(re_list)
                               TYPE REF TO lcl_reading_list,
         create_from_file RETURNING VALUE(re_list)
                               TYPE REF TO lcl_reading_list
                            RAISING cx_ixml_parse_error.

      METHODS:
         add_book IMPORTING im_isbn      TYPE string
                            im_title     TYPE string
                            im_authors   TYPE string_table
                            im_publisher TYPE string,
         display,
         serialize RAISING cx_sy_file_io.

   PRIVATE SECTION.
      TYPE-POOLS: ixml.
      TYPES: ty_xml_line(1024) TYPE x.

      CONSTANTS: CO_OPEN_DIALOG TYPE i VALUE 1,
                 CO_SAVE_DIALOG TYPE i VALUE 2.

      CLASS-DATA:
         factory        TYPE REF TO if_ixml,
         stream_factory TYPE REF TO if_ixml_stream_factory.

      DATA: xml_document TYPE REF TO if_ixml_document,
            list_node    TYPE REF TO if_ixml_element,
            books_node   TYPE REF TO if_ixml_element.

      METHODS:
         constructor,
         upload_list_file RAISING cx_ixml_parse_error,
         get_filename     IMPORTING im_dialog_type TYPE i
                          RETURNING VALUE(re_filename)
                               TYPE string.
ENDCLASS.

CLASS lcl_reading_list IMPLEMENTATION.
   METHOD class_constructor.
*     Obtain a reference to the iXML factory object:
      factory = cl_ixml=>create( ).

*     Obtain a reference to an iXML istream factory:
      stream_factory =
         factory->create_stream_factory( ).
   ENDMETHOD.

   METHOD constructor.
*     Create an instance of a DOM-based XML document:
      xml_document = factory->create_document( ).
   ENDMETHOD.

   METHOD create_new_list.
*     Create an instance of the reading list:
      CREATE OBJECT re_list.

*     Build the "ReadingList" root element:
      re_list->list_node =
         re_list->xml_document->create_simple_element(
            name = 'ReadingList'
            parent = re_list->xml_document ).

*     Build the "Topic" element:
      re_list->xml_document->create_simple_element(
         name = 'Topic'
         value = im_topic
         parent = re_list->list_node ).

*     Build the "RecommendedBooks" element:
      re_list->books_node =
         re_list->xml_document->create_simple_element(
            name = 'RecommendedBooks'
            parent = re_list->list_node ).
   ENDMETHOD.

   METHOD create_from_file.
*     Create an instance of the reading list:
      CREATE OBJECT re_list.

*     Upload a pre-existing reading list file into context:
      re_list->upload_list_file( ).

*     Obtain references to the index nodes:
      re_list->list_node =
         re_list->xml_document->get_root_element( ).

      re_list->books_node =
         re_list->list_node->find_from_name(
            name = 'RecommendedBooks' ).
   ENDMETHOD.

   METHOD upload_list_file.
*     Method-Local Data Declarations:
      DATA: lt_files       TYPE filetable,
            lv_retcode     TYPE i,
            lv_filename    TYPE string,
            lv_filesize    TYPE i,
            lt_xml         TYPE TABLE
                             OF ty_xml_line,
            lr_istream     TYPE REF TO if_ixml_istream,
            lr_parser      TYPE REF TO if_ixml_parser,
            lr_parse_error TYPE REF TO if_ixml_parse_error,
            lv_error_code  TYPE i,
            lv_reason      TYPE string.

*     Let the user select a file on their workstation:
      lv_filename = get_filename( CO_OPEN_DIALOG ).

*     Upload the file from the client's workstation:
      CALL METHOD cl_gui_frontend_services=>gui_upload
         EXPORTING
            filename   = lv_filename
            filetype   = 'BIN'
         IMPORTING
            filelength = lv_filesize
         CHANGING
            data_tab   = lt_xml
         EXCEPTIONS
            others     = 19.

*     Convert the raw XML content into an input stream:
      lr_istream =
         stream_factory->create_istream_itable(
            table = lt_xml
             size = lv_filesize ).
      IF lr_istream IS INITIAL.
         RAISE EXCEPTION TYPE cx_ixml_parse_error
            EXPORTING
               textid = cx_ixml_parse_error=>CX_IXML_PARSE_ERROR
               code   = sy-subrc
               reason = 'Could not read the XML list file!'.
      ENDIF.

*     Parse the XML file into a DOM tree:
      lr_parser =
         factory->create_parser(
            stream_factory = stream_factory
            istream        = lr_istream
            document       = xml_document ).

*     Check the results:
      lv_retcode = lr_parser->parse( ).
      IF lv_retcode NE 0.
*        Propagate the parse exception to the caller:
         lr_parse_error = lr_parser->get_error( 0 ).
         lv_error_code = lr_parse_error->get_number( ).
         lv_reason = lr_parse_error->get_reason( ).

         RAISE EXCEPTION TYPE cx_ixml_parse_error
            EXPORTING
               textid = cx_ixml_parse_error=>CX_IXML_PARSE_ERROR
               code   = lv_error_code
               reason = lv_reason.
      ENDIF.
   ENDMETHOD.

   METHOD get_filename.
*     Method-Local Data Declarations:
      DATA: lt_files    TYPE filetable,
            lv_retcode  TYPE i,
            lv_path     TYPE string,
            lv_filename TYPE string.

*     Let the user select a file from their workstation:
      CASE im_dialog_type.
         WHEN CO_OPEN_DIALOG.
            CALL METHOD
                 cl_gui_frontend_services=>file_open_dialog
               CHANGING
                  file_table = lt_files
                  rc         = lv_retcode
               EXCEPTIONS
                  others     = 5.

            READ TABLE lt_files INDEX 1 into re_filename.
         WHEN CO_SAVE_DIALOG.
            CALL METHOD
                 cl_gui_frontend_services=>file_save_dialog
               CHANGING
                  filename = lv_filename
                  path     = lv_path
                  fullpath = re_filename
               EXCEPTIONS
                  others   = 4.
      ENDCASE.
   ENDMETHOD.

   METHOD add_book.
*     Method-Local Data Declarations:
      DATA: lr_book_node    TYPE REF TO if_ixml_element,
            lv_author       TYPE string.

*     Build the new "Book" element:
      lr_book_node =
         xml_document->create_element( name = 'Book' ).

      lr_book_node->set_attribute(                    "ISBN Attribute
         name = 'isbn' value = im_isbn ).

      xml_document->create_simple_element(            "Title Element
         name = 'Title'
         value = im_title
         parent = lr_book_node ).

      xml_document->create_simple_element(            "Publisher Element
         name = 'Publisher'
         value = im_publisher
         parent = lr_book_node ).

      LOOP AT im_authors INTO lv_author.              "Author Elements
         xml_document->create_simple_element(
            name = 'Author'
            value = lv_author
            parent = lr_book_node ).
      ENDLOOP.

*     Add the "Book" element to the collection:
      books_node->append_child( lr_book_node ).
   ENDMETHOD.

   METHOD display.
*     Display the XML document in a browser:
      CALL FUNCTION 'SDIXML_DOM_TO_SCREEN'
         EXPORTING
            document    = xml_document
         EXCEPTIONS
            no_document = 1
            others      = 2.
   ENDMETHOD.

   METHOD serialize.
*     Method-Local Data Declarations:
      DATA: lr_ostream  TYPE REF TO if_ixml_ostream,
            lt_xml      TYPE TABLE OF ty_xml_line,
            lr_renderer TYPE REF TO if_ixml_renderer,
            lv_retcode  TYPE i,
            lv_filesize TYPE i,
            lv_filename TYPE string.

*     Create an output stream:
      lr_ostream =
         stream_factory->create_ostream_itable(
            table = lt_xml ).

*     Render the XML document to an output stream:
      lr_renderer =
         factory->create_renderer( ostream  = lr_ostream
                                   document = xml_document ).
      lv_retcode = lr_renderer->render( ).
      IF lv_retcode NE 0.
         RAISE EXCEPTION TYPE cx_sy_file_io.
      ENDIF.

*     Download the XML file to the user's presentation server:
      lv_filename = get_filename( CO_SAVE_DIALOG ).
      lv_filesize = lr_ostream->get_num_written_raw( ).

      CALL METHOD cl_gui_frontend_services=>gui_download
         EXPORTING
            bin_filesize = lv_filesize
            filename     = lv_filename
            filetype     = 'BIN'
         CHANGING
            data_tab     = lt_xml
         EXCEPTIONS
            others       = 24.

      IF sy-subrc NE 0.
         RAISE EXCEPTION TYPE cx_sy_file_io
            EXPORTING
               textid   = cx_sy_file_io=>CX_SY_FILE_ACCESS_ERROR
               filename = lv_filename.
      ENDIF.
   ENDMETHOD.
ENDCLASS.